"""编译核心模块"""
import os
import sys
import shutil
import subprocess
from pathlib import Path
from tempfile import TemporaryDirectory
from .config import Config
from .logger import get_logger
from .cleaner import Cleaner

class Compiler:
    """Python到SO的编译器"""
    
    def __init__(self, config: Config = None):
        self.config = config or Config()
        self.logger = get_logger("compiler", self.config.verbose)
        self.cleaner = Cleaner(self.config.verbose)
    
    def validate_file(self, file_path: Path) -> bool:
        """验证输入文件是否有效"""
        if not file_path.exists():
            self.logger.error(f"文件不存在: {file_path}")
            return False
        
        if file_path.suffix != ".py":
            self.logger.error(f"仅支持 .py 文件，当前文件: {file_path.suffix}")
            return False
            
        if not file_path.is_file():
            self.logger.error(f"{file_path} 不是一个文件")
            return False
            
        return True
    
    def generate_setup_code(self, module_name: str) -> str:
        """生成setup.py代码"""
        return f'''
from setuptools import setup, Extension
from Cython.Build import cythonize

ext_modules = [
    Extension(
        "{module_name}",
        sources=["{module_name}.py"],
        extra_compile_args={self.config.extra_compile_args},
        extra_link_args={self.config.extra_link_args}
    )
]

setup(
    name="{module_name}",
    ext_modules=cythonize(
        ext_modules,
        compiler_directives={{"language_level": {self.config.language_level}}},
        build_dir="build"
    ),
    script_args=["build_ext", "--inplace"]
)
'''
    
    def compile_file(self, py_file: str) -> bool:
        """编译单个Python文件为SO模块"""
        py_file = Path(py_file).resolve()
        
        # 验证文件
        if not self.validate_file(py_file):
            return False
            
        module_name = py_file.stem
        output_dir = self.config.output_dir or py_file.parent
        output_dir.mkdir(parents=True, exist_ok=True)
        
        self.logger.info(f"开始编译 {py_file.name} ...")
        
        # 使用临时目录进行编译
        with TemporaryDirectory() as tmp_dir:
            tmp_dir = Path(tmp_dir)
            tmp_py = tmp_dir / f"{module_name}.py"
            tmp_setup = tmp_dir / "setup.py"
            
            # 复制源文件到临时目录
            shutil.copy(py_file, tmp_py)
            
            # 生成setup.py
            setup_code = self.generate_setup_code(module_name)
            tmp_setup.write_text(setup_code)
            
            # 执行编译
            result = subprocess.run(
                [sys.executable, str(tmp_setup)],
                cwd=tmp_dir,
                capture_output=True,
                text=True
            )
            
            # 检查编译结果
            if result.returncode != 0:
                self.logger.error("编译失败:")
                self.logger.error(result.stderr)
                return False
            
            # 查找生成的.so文件
            so_files = list(tmp_dir.glob(f"{module_name}.*.so"))
            if not so_files:
                self.logger.error("未找到生成的 .so 文件")
                return False
            
            # 移动.so文件到目标目录
            so_file = so_files[0]
            target_so = output_dir / so_file.name
            
            # 如果目标文件已存在，先删除
            if target_so.exists():
                target_so.unlink()
                
            shutil.move(str(so_file), target_so)
            self.logger.info(f"编译成功: {target_so}")
        
        # 清理原目录中可能生成的中间文件
        self.cleaner.clean_compilation_files(module_name, py_file.parent)
        
        # 根据配置决定是否保留源文件
        if not self.config.keep_source:
            try:
                py_file.unlink()
                self.logger.info(f"已删除源文件: {py_file.name}")
            except Exception as e:
                self.logger.warning(f"删除源文件失败: {str(e)}")
        
        return True
    
    def compile_files(self, file_paths: list) -> dict:
        """编译多个文件，返回每个文件的编译结果"""
        results = {}
        for file_path in file_paths:
            results[str(file_path)] = self.compile_file(file_path)
        return results
